<?php
class Link extends AppModel {
	var $name = 'Link';
	
	var $actsAs=array('Ban');
	
	var $belongsTo = array(
		'User',	'Blog',	'Category'
	);

	var $hasMany = array(
		'Comment','Favorite','Tag',
		'Trackback',
		'Vote' 
	);
	
	var $pageObj;
	
    function beforeValidate(){
    	if(isset($this->data['Link']['url'])){
    		App::import('vendor','robot');
			
    		$this->pageObj=new Page($this->data['Link']['url'],$this,true);
						
			$this->data['Link']['url']=$this->pageObj->url;
    	}
    }
    
    function isContentShare(){    	
    	return !$this->pageObj->noShare;
    }
    
    function isFound($e){
    	return ($this->pageObj->errorMsg)? $this->pageObj->errorMsg : true ;
	}
    
    
    /**
	 * 
	 * chequea que la url no este repetida
	 * @param $linkData
	 */
    function isNotDuplicate($linkData){
       	if((isset($this->pageObj)&& $this->pageObj->duplicated)|| (!isset($linkData['url'])) ) return false;
    	
		$url=$linkData['url'];
		$urlSinWWW=str_replace('http://www.','http://',$url);
				
		return !$this->hasAny(array(
			'url'=>array_unique(array($url,$urlSinWWW))
		));
    }
    
    /**
	 * 
	 * chequea que el usuario no envie spam 
	 * @param $linkData
	 * TODO quitar numeros magicos
	 */
	function isNotAutobombo(){
		$DAYS=Configure::read('Monky.Autobombo.days');
		$ratio=0;
		$same_blog_sents=0;
	
		
		if($sents=$this->getTotalSents($DAYS,null)>0){
			$same_blog_sents=$this->getTotalSents($DAYS,$this->data['Blog']['key']);
			$ratio = $same_blog_sents/$sents;	
		}
			
				
		//envia posible spam
		if($sents > Configure::read('Monky.Autobombo.isSpam_sents') && $ratio > Configure::read('Monky.Autobombo.isSpam_ratio')){
			//loguear conducata sospechosa
			CakeLog::write('alerts','Meneame, warn, high ratio, process interrumped, user: '.$this->data['Link']['user_id']);
			return false;
		}

		if($sents > Configure::read('Monky.Autobombo.warning_sents') && $same_blog_sents > Configure::read('Monky.Autobombo.same_blog_sents') && $ratio > Configure::read('Monky.Autobombo.warning_ratio')){
			$this->warning=__('You have send too many links, you could recive negative karma',true);
		}
		
		return true;
	}
	
	function isVotable($link=null) {
				
		if(Visitor::isBot()) return false;

		$link=$this->prepareFields($link, array('status'));
				
		if(in_array($link['Link']['status'],array('abuse','autodiscard'))) return false;
		
		return $this->isLinkInVoteInterval($link);
	}
	
	private function isLinkInVoteInterval($link){
		$time_enabled=Configure::read('Monky.Votes.time_enabled');
		if($time_enabled<=0) return true;
		
		$link=$this->prepareFields($link, array('created'));
		
		$createdinterval=time()-getUnixtime($link['Link']['created']);
		return $createdinterval<Configure::read('Monky.Votes.time_enabled');
	}
	
	/**
	 * 
	 * Cuenta los envios de un usuario
	 * @param $days
	 * @param $blogKey si es null cuenta todos los envios, si tiene la llave cuenta los de ese blog
	 * @param $user_id id del usuario, si es null usa el $data[Link][user_id]
	 */
	function getTotalSents($days=null,$blogKey=null,$user_id=null){
		if(!$user_id){
			$user_id=$this->data['Link']['user_id'];
		}
						
		$conditions=array('Link.user_id'=>$user_id);
		if($days) $conditions['Link.created >']='date_sub(now(), interval '.$days.' day)';
						
		if($blogKey){		
			$conditions['blog_id']=$this->Blog->field('id',array('key'=>$blogKey));
		}
			
		return $this->find('count',array(
			'conditions'=>$conditions,
			'recursive'=>-1
		));
			
	}
    
	function setUrlValidate(){
		$this->validate['url']=array(
	   		array(
	   			'rule'=>'notEmpty',
	   			'message'=>__('Enter a url',true),
	   			'last' => true
	   		),array(
				'rule' => array('url', true),
    			'message'=>__('The url is invalid',true),
    			'last' => true
    		),array(
    			'rule' => array('isFound'),
    			'last' => true
			),array(
    			'rule' => array('isContentShare'),
    			'message'=>__('The site owner of the site doesnt wanna share the content',true),
    			'last' => true
    		),array(
	    		'rule'=> array('notBannedHostname'),
    			'message'=>__('The site is banned',true),
    			'last' => true
    		),array(
    			'rule' => array('isNotDuplicate'),
    			'message'=>__('The url has already been sent',true),
    			'last' => true
    		),array(
	    		'rule' => array('isNotAutobombo'),
    			'message'=>__('You have send too many links',true),
    			'last' => true
    		)  		
    	);
		
	}
	
	function setValidateStep1(){
		$this->setUrlValidate();
	}
	
	function setValidateStep2(){
		
		$this->validate=array(
			'title'=>array(
    			array(
    				'rule' =>array('minLength',15),
    				'message'=>__('Write at least 15 characters',true),
    				'allowEmpty' => false
    			)
    		),
    		'content'=>array(
    			array(
    				'rule' =>array('minLength',30),
    				'message'=>__('Enter a longer description, write at least 30 characters',true),
    				'allowEmpty' => false
    			)
    		),
    		'category_id' => array(
        		'rule' => array('comparison', '>', 0),  
        		'message' => __('Plase choose a category',true),
    			'allowEmpty' => false
    		)
    	);
    	
    	$this->setUrlValidate();
	}
	
	function vote($link_id,$user){
		$this->id=$link_id;
		
		if(!isset($user['vote'])||$user['vote']>0){
			if($user['karma']<0) $user['karma']=1;	
			$user['vote']=$user['karma'];
		}
		
		$this->_checkErrorInVote($user);	

		$this->addLinkVote($user);
		
		$this->incrementVotes($user);
		
		$this->recursive=-1;
		$updatedLink=$this->read(array('id','votes','anonymous','negatives','karma'));
		return $updatedLink;
	}
	
	function incrementVotes($user){
		$toUpdate='anonymous';
			
		if ($user['id']>0 ) {
			if ($user['vote'] > 0) {
				$toUpdate='votes';
			} else {
				$toUpdate='negatives';
			}
		} 
		$this->updateAll(array($toUpdate=>$toUpdate.'+1'),array('id'=>$this->id),-1);
		
		if ($this->field('status') != 'published') {
			// If not published we update karma and count all votes
			$this->updateAll(array('karma'=>'karma+'.$this->getKarmaValue($user)),array('id'=>$this->id),-1);
		}
				
	}
	
	/**
	 * 
	 * obtiene el valor del karma en base al voto del usuario
	 */
	function getKarmaValue($user){
		$karma_value = 0;
		if ($this->field('status') != 'published') {
			$affinity = $this->User->getAffinity($this->field('user_id'), $user['id']);
			if($user['vote'] < 0 && $user['id']>0) {
				if ($user['id'] != $this->field('user_id') && $affinity <  0 ) {
					$karma_value = round(min(-5, $user['karma'] *  $affinity/100));
				} else {
					$karma_value = round(-$user['karma']);
				}
			} else {
				if ($user['id']>0 && $user['id'] != $this->field('user_id')&& $affinity > 0 ) {
					$karma_value = $value = round(max($user['karma'] * $affinity/100, 5));
				} else {
					$karma_value=round($user['vote']);
				}
			}
		} 

		return $karma_value;
	}	
	
	function countVoteClons($days=7,$user_id) {
		return $this->Vote->countClons('links', $this->id, $user_id,$days);
	}
	
	private function addLinkVote($user){
		$this->Vote->create(array(
				'type'=>'links',
				'value'=>$user['vote'],
				'ip_int'=>Visitor::ip_int(),
				'link_id'=>$this->id,
				'user_id'=>$user['id'],
		));
		$this->Vote->save();
//		//TODO REFACTOR PARA QUITAR LA SENTENCIA SQL
//		// HIGH_PRIORITY to avoid duplicates votes from people clicking very fast on purpose
//		$this->query("INSERT HIGH_PRIORITY INTO votes (vote_type, vote_user_id, vote_link_id, vote_value, vote_ip_int) VALUES 
//			('".$data['vote_type']."', ".$data['vote_user_id'].", ".$data['vote_link_id'].", ".$data['vote_value'].", ".$data['vote_ip_int'].")");
		 
	}
	
    /**
     * 
     * chequea que este seteado el id, que no este baneada la ip,
     * que sea votable, que se permitan votos anonimos, 
     * que si no fue publicada no supere ceirta cantidad de votos anonimos
     * que no se vote usando clons y la frecuencia de votos
     */
	private function _checkErrorInVote($user){
		
		try{
			$this->checkLinkExist();
			$this->User->checkIpNotBanned();
			$this->checkIsVotable();
		
			if ($user['id']==0){
				$this->_checkErrorInAnonVote();
			}
	
			if($this->Vote->userHasVote($this->id,$user['id'])){
				if($user['id']>0){
					throw new ErrorVoteException(__('you have already vote with this IP or username',true));
				}else{
					throw new ErrorVoteException(__('the IP have already vote this link',true));		
				}
			}
						
			$this->checkLinkVoteClons($user);
			
			$this->_checkVoteFreq($user);

			} catch (UserErrorException $exception){
			throw new ErrorVoteException($exception->getMessage());
		} catch (LinkErrorException $exception){
			throw new ErrorVoteException($exception->getMessage());
		}
		
	}
	
	function checkLinkExist(){
		if(!$this->hasAny(array('id'=>$this->id))){
			throw new LinkErrorException(__('Link doesnt exist',true));
		}
	}
		
	function checkLinkIsVotable(){
		if(!$this->isVotable()){
			throw new ErrorVoteException(__('votes closed',true));
		}
	}
	
	function checkLinkVoteClons($user){
		if($this->Vote->countLinkVoteClons($this->id,$user['id'],5)>0){
			$user=$this->User->getLoggedUser();
			CakeLog::write('alerts', 'Meneame: clon vote link, id: '.$this->id.', user: '.$user['user_id']);
			throw new ErrorVoteException(__('you can not vote with clons',true));
		}
	}
		
	function _checkVoteFreq($user){

		$votes_freq = $this->Vote->getVoteFreq($user);

		$freq=($user['id']>0)? 3 : 2;

		// Allow to play a little more if published
		if ($this->field('status') == 'published')  $freq *= 2; 

		if ($votes_freq > $freq) {
			if ($user['id']>0 && $user['karma'] > 4 && $this->field('status') != 'published') {
		    	// Crazy votes attack, decrease karma
				// she does not deserve it :-)
				
    				$this->User->updateAll(array('karma'=>'User.karma - '.Configure::read('Monky.Votes.abuse_karma_decrease')),array('id'=>$user['id']));
    			
				throw new ErrorVoteException(__('easy cowboy!'). ', ' . sprintf(__('your karma has decreased %s points',true),Configure::read('Monky.Votes.abuse_karma_decrease')));
			}
			throw new ErrorVoteException( __('easy cowboy!',true));
		}
				
	}
	
	function _checkErrorInAnonVote(){
		if(!Configure::read('Monky.Votes.anon_enable')) {
			return __('Anon votes are disabled',true);
		}
		
		// Check that there are not too much annonymous votes
		$anon_to_user_votes=Configure::read('Monky.Votes.anon_to_user');
		// Allow more anon votes if published.
		if ($this->field('status') == 'published') $anon_to_user_votes = max(3, $anon_to_user_votes); 
		 
		if ($this->field('anonymous') >  ($this->field('votes') * $anon_to_user_votes)) {
			throw new ErrorVoteException(__('Too many anon votes in this link, register to vote or try later',true));
		}
		
	}

}

class LinkErrorException extends Exception {}
class ErrorVoteException extends Exception {}
